/*	Matt Viehdorfer
 *  CS575 - Spring 2012
 *  Project 9 - OpenCL / OpenGL Particle System
 */

#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <time.h>
#include <omp.h>            //Incorrectly reports an error in OSX - ignore it
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;

#define _USE_MATH_DEFINES
#include <math.h>

#ifdef WIN32
#include <windows.h>
#include <Shlwapi.h>        //Filepath Stuff
#include <atlstr.h>         //Filepath stuff
#include "Win\glew.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include "Win\glut.h"
#include "Win\glui.h"
#include "Win\cl.h"
#include "Win\cl_gl.h"
#include "Win\cl.h"
#include "Win\cl_gl.h"
#include "Win\cl_gl_ext.h"
#include "Win\cl_platform.h"
#pragma warning(disable:4996)
#endif

#ifdef __APPLE__
#include <mach-o/dyld.h>    // _NSGetExecutablePath
#include <GLUI/glui.h>
#include <GLUT/glut.h>
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLDevice.h>
#include <OpenCL/opencl.h>
#include <OpenCL/cl_gl_ext.h>
#endif

// gets rid of annoying "deprecated conversion from string constant blah blah" warning
#pragma GCC diagnostic ignored "-Wwrite-strings"


#include "helpers.h"


// title of these windows:
const char *WINDOWTITLE = { "OpenCL/OpenGL Particle System -- Matt Viehdorfer" };
const char *GLUITITLE   = { "User Interface Window" };

// random parameters:				
const float XMIN = 	{ -100.0 };
const float XMAX = 	{  100.0 };
const float YMIN = 	{ -100.0 };
const float YMAX = 	{  100.0 };
const float ZMIN = 	{ -100.0 };
const float ZMAX = 	{  100.0 };

const float VMIN =	{   -100. };
const float VMAX =	{    100. };
const float startingPos[] = { 0, 0, 0 };

//const int NUMPARTICLES = 1*1024*1024;
//const int NUMPARTICLES = 524288;
//const int LOCAL_SIZE    = 32;
#define NUMPARTICLES	1024
#define TIME_COUNT		20000
const int LOCAL_SIZE    = 32;

#ifdef WIN32
const char *CL_FILE_NAME = { "../particles.cl" };
#endif

#ifndef WIN32
const char *CL_FILE_NAME = { "particles.cl" };
#endif



const int GLUITRUE  = { true  };
const int GLUIFALSE = { false };

#define ESCAPE		0x1b

const int INIT_WINDOW_SIZE = { 700 };		// window size in pixels

const float ANGFACT = { 1. };
const float SCLFACT = { 0.005f };
const float MINSCALE = { 0.001f };

const int LEFT   = { 4 };
const int MIDDLE = { 2 };
const int RIGHT  = { 1 };

// variables to compute frames per second
int frame; //number of frames since last computed the frame rate
long timeFPS; //the current number of milliseconds
long timebase = 0; //time when last computed the frame rate

enum Projections
{
	ORTHO,
	PERSP
};

enum ButtonVals
{
	GO,
	RESET,
	QUIT
};

const float BACKCOLOR[ ] = { 0., 0., 0., 0. };

/*const GLfloat AXES_COLOR[ ] = { 1., .5, 0. };
const GLfloat AXES_WIDTH   = { 3. };*/

// Particle structure
struct particle
{
	float x0, y0, z0;       // starting location
	float vx0, vy0, vz0;    // starting velocity
	float r0, g0, b0;       // starting color
	float t0, t1;           // birth, death time
	float x, y, z;          // current location
	float vx, vy, vz;       // current velocity
	float r, g, b;          // current color
	float Direction;
	float Acceleration;
	float Deceleration;
	float ScaleP;
};

// structs we will need later:
struct xyzw
{
	float x, y, z, w;
};

struct rgba
{
	float r, g, b, a;
};


// non-constant global variables:
int	ActiveButton;		// current button that is down
//GLuint	AxesList;		// list to hold the axes
//int	AxesOn;			// ON or OFF
GLUI *	Glui;			// instance of glui window
int	GluiWindow;		// the glut id for the glui window
int	MainWindow;		// window id for main graphics window
GLfloat	RotMatrix[4][4];	// set by glui rotation widget
float	Scale, Scale2;		// scaling factors
GLuint	SphereList;
int	WhichProjection;	// ORTHO or PERSP
int	Xmouse, Ymouse;		// mouse values
float	Xrot, Yrot;		// rotation angles in degrees
float	TransXYZ[3];		// set by glui translation widgets

double	ElapsedTime;
double	currFPS;
//int		ShowPerformance;
//int		ShowFPS;

size_t GlobalWorkSize[3] = { NUMPARTICLES, 1, 1 };
size_t LocalWorkSize[3]  = { LOCAL_SIZE,    1, 1 };

GLuint			hPobj;
GLuint			hCobj;
cl_mem			dPobj;
struct xyzw *	hVel;
cl_mem			dVel;
float *			hRand;
cl_mem			dRand;
cl_command_queue	CmdQueue;
cl_device_id		Device;
cl_kernel		Kernel;
cl_platform_id		Platform;
cl_program		Program;
cl_platform_id		PlatformID;

double compTimes[TIME_COUNT];
int compNum = 0;
double time0, time1;
double startTime, endTime;

// function prototypes:
inline
float
SQR( float x )
{
	return x * x;
}

void	Animate( );
//void	Axes( float );
void	Buttons( int );
void	Display( );
void	DoRasterString( float, float, float, char * );
void	DoStrokeString( float, float, float, float, char * );
void	InitCL( );
void	InitGlui( );
void	InitGraphics( );
void	InitLists( );
bool	IsCLExtensionSupported( const char * );
void	Keyboard( unsigned char, int, int );
void	MouseButton( int, int, int, int );
void	MouseMotion( int, int );
void	PrintCLError( cl_int, char * = "", FILE * = stderr );
void	Quit( );
float	Ranf( float, float );
void	Reset( );
void	ResetParticles( );
void	Resize( int, int );
void	Traces( int );
void	Visibility( int );
void	LogTimeOut( );